use std::marker::{PhantomPinned, Unpin};
use std::pin::Pin;
use std::ptr::NonNull;

#[cfg(test)]
mod tests {
    use super::*;

    #[test]
    fn test_pin() {
        start();
    }
}

fn start() {
    let string = "hello".to_string();
    println!("{:p}", &string);
    let mut unmoved = Unmovable::new(string);
    assert_eq!(unmoved.slice, NonNull::from(&unmoved.data));

    let string = "world".to_string();
    println!("{:p}", &string);
    let mut new_unmoved = Unmovable::new(string);

    println!(
        "{:?}\t{:?} \n{:?}\t{:?}",
        unmoved,
        unsafe { unmoved.slice.as_ref() },
        new_unmoved,
        unsafe { new_unmoved.slice.as_ref() }
    );
    println!(
        "{:p}\t{:p} \n{:p}\t{:p}",
        &unmoved.data, unmoved.slice, &new_unmoved.data, new_unmoved.slice
    );

    println!("\n");
    // std::mem::swap(&mut *unmoved, &mut *new_unmoved);
    std::mem::swap(&mut unmoved, &mut new_unmoved);

    println!(
        "{:?}\t{:?} \n{:?}\t{:?}",
        unmoved,
        unsafe { unmoved.slice.as_ref() },
        new_unmoved,
        unsafe { new_unmoved.slice.as_ref() }
    );

    println!(
        "{:p}\t{:p} \n{:p}\t{:p}",
        &unmoved.data, unmoved.slice, &new_unmoved.data, new_unmoved.slice
    );

    let mut test1 = Test::new("test1");
    test1.init();
    let mut test2 = Test::new("test2");
    test2.init();

    println!("a: {}, b: {}", test1.a(), test1.b());
    println!("a: {}, b: {}", test2.a(), test2.b());
    println!(
        "a: {:p} {:p}, b: {:p} {:p}",
        &test1.a, test1.b, &test2.a, test2.b
    );
    println!("\n");
    std::mem::swap(&mut test1, &mut test2);
    println!("a: {}, b: {}", test1.a(), test1.b());
    println!("a: {}, b: {}", test2.a(), test2.b());
    println!(
        "a: {:p} {:p}, b: {:p} {:p}",
        &test1.a, test1.b, &test2.a, test2.b
    );
}

#[derive(Debug)]
struct Unmovable {
    data: String,
    slice: NonNull<String>,
    _pin: PhantomPinned,
}

// impl Unpin for Unmovable {}

impl Unmovable {
    fn new(data: String) -> Pin<Box<Self>> {
        let unmovable = Self {
            data,
            slice: NonNull::dangling(),
            _pin: PhantomPinned,
        };
        let mut boxed = Box::pin(unmovable);
        let slice = NonNull::from(&boxed.data);
        unsafe {
            Pin::get_unchecked_mut(boxed.as_mut()).slice = slice;
        }
        boxed
    }
}

#[derive(Debug)]
struct Test {
    a: String,
    b: NonNull<String>,
}

impl Test {
    fn new(txt: &str) -> Self {
        Test {
            a: String::from(txt),
            b: NonNull::dangling(),
        }
    }
    fn init(&mut self) {
        self.b = NonNull::from(&self.a);
    }
    fn a(&self) -> &str {
        &self.a
    }
    fn b(&self) -> &String {
        unsafe { self.b.as_ref() }
    }
}
